#pragma once
#include "../common/mq_msg/mq_msg.pb.h"
#include "../common/helper.hpp"
#include "../common/log.hpp"

#include <iostream>
#include <memory>
#include <sstream>
#include <unordered_map>

namespace xzt
{
    using std::string;

    class Binding
    {
    public:
        using ptr = std::shared_ptr<Binding>;
    public:
        Binding(const string& exchange_name, const string& msgqueue_name, const string& binding_key)
            : _exchange_name(exchange_name), _msgqueue_name(msgqueue_name), _binding_key(binding_key) {}

    
    public:
        string _exchange_name;
        string _msgqueue_name;
        string _binding_key;
    };

    // msgqueue和binding的映射关系是一一对应的
    using MsgQueueBindingMap = std::map<string, Binding::ptr>;
    // exchange和msgqueue的映射关系是一对多的
    using BindingMap = std::map<string, MsgQueueBindingMap>;

    class BindingMapper
    {
    public:
        BindingMapper(const string& dbfile) : _sql_helper(dbfile) 
        {
            // 若数据库文件不存在，则创建目录
            string path = xzt::FileHelper::parentDirectory(dbfile);
            xzt::FileHelper::createDirectory(path);
            // 声明数据库文件
            _sql_helper.open();
            // 创建表
            createTable();
        }

        void createTable()
        {
            string sql = "CREATE TABLE IF NOT EXISTS binding_table (\
            exchange_name varchar(32), msgqueue_name varchar(32), \
            binding_key varchar(128), \
            PRIMARY KEY (exchange_name, msgqueue_name, binding_key))";

            if(!_sql_helper.exec(sql, nullptr, nullptr))
            {
                ELOG("BindingMapper::createTable() -> create binding table failed");
            }
        }

        void removeTable()
        {
            string sql = "DROP TABLE IF EXISTS binding_table";

            if(!_sql_helper.exec(sql, nullptr, nullptr))
            {
                ELOG("BindingMapper::removeTable() -> remove binding table failed");
            }
        }

        bool insert(const Binding::ptr& binding)
        {
            std::stringstream ss;
            ss << "INSERT INTO binding_table (exchange_name, msgqueue_name, binding_key) VALUES (";
            ss << "'" << binding->_exchange_name << "', '" << binding->_msgqueue_name << "', '" << binding->_binding_key << "');";
        
            if(!_sql_helper.exec(ss.str(), nullptr, nullptr))
            {
                ELOG("BindingMapper::insert() -> insert binding failed");
                return false;
            }
            return true;
        }

        bool remove(const string& exchange_name, const string& msgqueue_name)
        {
            std::stringstream ss;
            ss << "DELETE FROM binding_table WHERE exchange_name = '" << exchange_name << "' AND msgqueue_name = '" << msgqueue_name << "';";
        
            if(!_sql_helper.exec(ss.str(), nullptr, nullptr))
            {
                ELOG("BindingMapper::remove() -> remove binding failed");
                return false;
            }
            return true;
        }

        bool removeExchangeBindings(const string& exchange_name)
        {
            std::stringstream ss;
            ss << "DELETE FROM binding_table WHERE exchange_name = '" << exchange_name << "';";
        
            if(!_sql_helper.exec(ss.str(), nullptr, nullptr))   // 如果binding是非持久化存储，这里的删除操作依然返回SQLITE_OK，但实际上并没有删除任何数据
            {
                ELOG("BindingMapper::removeExchangeBindings() -> remove exchange bindings failed");
                return false;
            }
            return true;
        }

        bool removeMsgQueueBindings(const string& msgqueue_name)
        {
            std::stringstream ss;
            ss << "DELETE FROM binding_table WHERE msgqueue_name = '" << msgqueue_name << "';";
        
            if(!_sql_helper.exec(ss.str(), nullptr, nullptr))
            {
                ELOG("BindingMapper::removeMsgQueueBindings() -> remove msgqueue bindings failed");
                return false;
            }
            return true;
        }

        BindingMap recovery()
        {
            BindingMap result;

            string sql = "SELECT exchange_name, msgqueue_name, binding_key FROM binding_table";

            if(!_sql_helper.exec(sql, &BindingMapper::_SelectCallback, &result))
            {
                ELOG("BindingMapper::recovery() -> select binding failed");
                return BindingMap();
            }
            return result;
        }
    private:
        static int _SelectCallback(void *arg, int argc, char **argv, char **col_name)
        {
            BindingMap* p_bm = static_cast<BindingMap*>(arg);
            Binding::ptr bp = std::make_shared<Binding>(argv[0], argv[1], argv[2]);
            
            // 为了防止交换机相关的binding信息已经存在，如果直接创建队列映射进行插入，可能会导致数据覆盖
            // 因此先获得交换机对应的队列映射容器，再插入<queue_name,binging_ptr>映射
            // 但是若没有见换机的对应信息，这里的引用会保证不存在先创建
            MsgQueueBindingMap& mqbm = (*p_bm)[bp->_exchange_name];
            mqbm.insert(std::make_pair(bp->_msgqueue_name, bp));
            return 0;
        }
    private:
        SqliteHelper _sql_helper;
    };

    class BindingManager
    {
    public:
        using ptr = std::shared_ptr<BindingManager>;
    public:
        BindingManager(const string& dbfile) : _mapper(dbfile) 
        {
            // 从数据库中恢复binding信息
            _bindings =_mapper.recovery();
        }

        bool bind(const string& exchange_name, const string& msgqueue_name, const string& binding_key, bool durable = true)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            // 先检查是否已经存在该binding，避免重复绑定
            auto it = _bindings.find(exchange_name);
            if(it != _bindings.end() && it->second.find(msgqueue_name) != it->second.end())
            {
                ILOG("BindingManager::bind() -> binding already exists");
                return true;
            }

            // 构造binding对象
            Binding::ptr bp = std::make_shared<Binding>(exchange_name, msgqueue_name, binding_key);

            // 判断是否需要持久化存储
            if(durable)
            {
                // 插入数据库
                if(!_mapper.insert(bp))
                {
                    ELOG("BindingManager::bind() -> insert binding failed");
                    return false;
                }
            }
            // 将binding对象添加到内存中
            // 为了防止交换机相关的binding信息已经存在，如果直接创建队列映射进行插入，可能会导致数据覆盖
            // 所以用引用的方式获得交换机对应的队列映射容器，再插入<queue_name,binging_ptr>映射
            MsgQueueBindingMap& mqbm = _bindings[exchange_name];
            mqbm.insert(std::make_pair(msgqueue_name, bp));
            return true;
        }

        bool unbind(const string& exchange_name, const string& msgqueue_name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            // 先检查是否存在该binding，避免重复解绑
            auto it = _bindings.find(exchange_name);
            if(it == _bindings.end() || it->second.find(msgqueue_name) == it->second.end())
            {
                ILOG("BindingManager::unbind() -> binding not exists");
                return true;
            }

            // 从内存中删除binding对象
            it->second.erase(msgqueue_name);

            // 从数据库中删除binding对象
            if(!_mapper.remove(exchange_name, msgqueue_name))
            {
                ELOG("BindingManager::unbind() -> remove binding failed");
                return false;
            }
            return true;
        }

        bool unbindExchange(const string& exchange_name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            // 先检查是否存在该exchange，避免重复解绑
            auto it = _bindings.find(exchange_name);
            if(it == _bindings.end())
            {
                ILOG("BindingManager::unbindExchange() -> exchange not exists");
                return true;
            }

            // 从内存中删除exchange和msgqueue的binding对象
            _bindings.erase(exchange_name);

            // 从数据库中删除exchange的binding对象
            // 如果binding是非持久化存储，这里的删除操作依然返回SQLITE_OK，但实际上并没有删除任何数据
            if(!_mapper.removeExchangeBindings(exchange_name))
            {
                ELOG("BindingManager::unbindExchange() -> remove exchange bindings failed");
                return false;
            }
            return true;
        }

        bool unbindMsgQueue(const string& msgqueue_name)
        {
            std::lock_guard<std::mutex> lock(_mutex);

            // 从内存中删除msgqueue的binding对象
            for(auto it = _bindings.begin(); it != _bindings.end();)
            {
                auto& mqbm = it->second;
                auto mit = mqbm.find(msgqueue_name);
                if(mit != mqbm.end())   // 找到了该msgqueue的binding
                {
                    mqbm.erase(mit);
                }
                if(mqbm.empty())    // 该exchange下没有binding了，删除该exchange
                {
                    ILOG("BindingManager::unbindMsgQueue() -> exchange %s has no binding, remove it", it->first.c_str());
                    it = _bindings.erase(it);
                }
                else    // 继续遍历下一个exchange
                {
                    ++it;
                }
            }

            // 从数据库中删除msgqueue的binding对象
            if(!_mapper.removeMsgQueueBindings(msgqueue_name))
            {
                ELOG("BindingManager::unbindMsgQueue() -> remove msgqueue bindings failed");
                return false;
            }
            return true;
        }

        // 获取指定交换机的所有binding信息，返回一个队列映射容器
        MsgQueueBindingMap getExchangeBindings(const string& exchange_name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            MsgQueueBindingMap result;
            auto it = _bindings.find(exchange_name);
            if(it != _bindings.end())
            {
                result = it->second;
            }
            return result;
        }

        // 获取指定的交换机和队列的binding信息
        Binding::ptr getBinding(const string& exchange_name, const string& msgqueue_name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _bindings.find(exchange_name);
            if(it != _bindings.end())
            {
                auto mit = it->second.find(msgqueue_name);
                if(mit != it->second.end())
                {
                    return mit->second;
                }
            }
            return Binding::ptr();
        }

        // 判断指定交换机和队列是否存在binding关系
        bool exists(const string& exchange_name, const string& msgqueue_name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _bindings.find(exchange_name);
            if(it == _bindings.end() || it->second.find(msgqueue_name) == it->second.end())
            {
                return false;
            }
            return true;
        }

        size_t size()
        {
            size_t count = 0;
            std::lock_guard<std::mutex> lock(_mutex);
            for(auto iter: _bindings)
            {
                count += iter.second.size();
            }
            return count;
        }

        void clear()
        {
            std::lock_guard<std::mutex> lock(_mutex);
            _bindings.clear();
            _mapper.removeTable();
        }

    private:
        std::mutex _mutex;
        BindingMap _bindings;
        BindingMapper _mapper;
    };
}